package com.yulei.framework.ioc.core;

import com.yulei.framework.ioc.annotation.DIbyName;
import com.yulei.framework.ioc.bean.BeanDefinition;
import com.yulei.framework.ioc.bean.ConstructorArg;
import com.yulei.framework.ioc.utils.BeanUtils;
import com.yulei.framework.ioc.utils.ClassUtils;
import com.yulei.framework.ioc.utils.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author yulei
 * date 2023/11/21 20:26
 */
public class BeanFactoryImpl implements BeanFactory {

    private static final ConcurrentHashMap<String, Object> beanMap = new ConcurrentHashMap<>();

    private static final ConcurrentHashMap<String, BeanDefinition> beanDefineMap = new ConcurrentHashMap<>();

    private static final Set<String> beanNameSet = Collections.synchronizedSet(new HashSet<>());

    @Override
    public Object getBean(String name) throws Exception {
        // 查找对象是否已经实例化过
        Object bean = beanMap.get(name);
        if (bean != null) {
            return bean;
        }
        // 如果没有实例化，那就需要调用createBean来创建对象
        bean = createBean(beanDefineMap.get(name));

        if (bean != null) {
            // 对象创建成功以后，注入对象需要的参数
            if (this instanceof AnnotationApplicationContext) {
                populatebean1(bean);
            } else {
                populatebean(bean);
            }
            // 再把对象存入Map方便下次使用
            beanMap.put(name, bean);
        }

        // 结束返回
        return bean;
    }

    // 容器初始化，调用此方法，把对象的BeanDefination数据结构，存储起来
    protected void registerBean(String name, BeanDefinition bd) {
        beanDefineMap.put(name, bd);
        beanNameSet.add(name);
    }

    private Object createBean(BeanDefinition beanDefinition) throws Exception {
        String beanName = beanDefinition.getClassName();
        Class clz = ClassUtils.loadClass(beanName);
        if (clz == null) {
            throw new Exception("can not find bean by beanName");
        }
        List<ConstructorArg> constructorArgs = beanDefinition.getConstructorArgs();
        if (constructorArgs != null && !constructorArgs.isEmpty()) {
            List<Object> objects = new ArrayList<>();
            for (ConstructorArg constructorArg : constructorArgs) {
                objects.add(getBean(constructorArg.getRef()));
            }
            return BeanUtils.instanceByCglib(clz, clz.getConstructor(), objects.toArray());
        } else {
            return BeanUtils.instanceByCglib(clz, null, null);
        }
    }

    private void populatebean(Object bean) throws Exception {
        Field[] fields = bean.getClass().getSuperclass().getDeclaredFields();
        if (fields != null && fields.length > 0) {
            for (Field field : fields) {
                String beanName = field.getName();
                beanName = uncapitalize(beanName);
                if (beanNameSet.contains(field.getName())) {
                    Object fieldBean = getBean(beanName);
                    if (fieldBean != null) {
                        ReflectionUtils.injectField(field, bean, fieldBean);
                    }
                }
            }
        }
    }

    // 添加 DIbyName 注解的 字段才进行注入
    private void populatebean1(Object bean) throws Exception {
        Field[] fields = bean.getClass().getSuperclass().getDeclaredFields();
        if (fields != null && fields.length > 0) {
            for (Field field : fields) {
                DIbyName annotation = field.getAnnotation(DIbyName.class);
                if (annotation != null) {
                    String beanName = !annotation.name().isEmpty() ? annotation.name() : uncapitalize(field.getName());
                    if (beanNameSet.contains(beanName)) {
                        Object fieldBean = getBean(beanName);
                        if (fieldBean != null) {
                            ReflectionUtils.injectField(field, bean, fieldBean);
                        }
                    }
                }
            }
        }
    }

    public static String uncapitalize(String str) {
        if (str == null || str.isEmpty()) {
            return str;
        } else {
            return Character.toLowerCase(str.charAt(0)) + str.substring(1);
        }
    }
}
